import cv2
import sys
import numpy as np
import pyautogui
import time
import os
import sys
from PIL import Image, ImageTk
import win32gui
import win32con
import win32ui
import json
import time  # 新增导入 time 模块
import tkinter as tk
from tkinter import ttk, scrolledtext
from tkinter import messagebox
from PIL import Image, ImageTk
import re
import uuid
import pystray  # 新增导入 pystray 模块
import keyboard  # 导入 keyboard 模块
import ntplib  # 新增导入 ntplib 模块
import random  # 导入 random 模块
import ctypes
import logging  # 添加日志模块
from tree_view import TemplateTreeView  # 添加导入语句

class OnmyojiAuto:
    def _load_config(self):
        """加载配置文件
        
        Returns:
            dict: 配置信息
        """
        config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            print(f"加载配置文件失败: {str(e)}，使用默认配置")
            return {
                "mode": "run",
                "run_mode": {
                    "skip_game_window_check": False,
                    "confidence_threshold": 0.7,
                    "click_interval": 1.0
                }
            }
    def __init__(self, window_title="MuMu模拟器12"):
        """初始化自动化工具
        
        Args:
            window_title: 游戏窗口标题关键字，默认为""
        """
        self.window_title = window_title
        self.window_handle = None
        self.running = False
        self.selected_template = None
        
        # 加载配置文件
        self.config = self._load_config()
        self.mode = self.config.get("mode", "run")
        mode_config = self.config.get(f"{self.mode}_mode", {})
        
        # 从配置文件加载参数
        self.confidence_threshold = mode_config.get("confidence_threshold", 0.7)  # 图像匹配置信度阈值
        self.click_interval = mode_config.get("click_interval", 1.0)  # 点击间隔时间(秒)
        self.skip_game_window_check = mode_config.get("skip_game_window_check", False)  # 是否跳过游戏窗口检测
        
        self.templates_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
        
        # 确保模板目录存在
        if not os.path.exists(self.templates_dir):
            os.makedirs(self.templates_dir)
            print(f"已创建模板目录: {self.templates_dir}")
        
        # 初始化窗口句柄
        if not self.skip_game_window_check:
            self._find_game_window()
        else:
            print("调试模式：跳过游戏窗口检测")
            # 在调试模式下使用桌面窗口作为默认窗口
            self.window_handle = win32gui.GetDesktopWindow()
    
    def _find_game_window(self, window_title=None):
        """查找游戏窗口并获取句柄（支持正则表达式匹配）"""
        import re
        if window_title is None:
            window_title = self.window_title
        pattern = re.compile(window_title)
        
        def callback(hwnd, extra):
            if win32gui.IsWindowVisible(hwnd):
                title = win32gui.GetWindowText(hwnd)
                if pattern.search(title):
                    self.window_handle = hwnd
                    return False
            return True
        
        # 重试3次查找窗口
        for attempt in range(3):
            win32gui.EnumWindows(callback, None)
            if self.window_handle:
                break
            time.sleep(1)
        
        if self.window_handle:
            print(f"找到游戏窗口: {window_title} (句柄: {self.window_handle})")
            # 将窗口置于前台
            win32gui.SetForegroundWindow(self.window_handle)
        else:
            print(f"未找到游戏窗口: {window_title}，请确保游戏已启动")

    def screenshot(self):
        """获取游戏窗口的截图
        
        Returns:
            numpy.ndarray: 游戏窗口的截图图像
        """
        if not self.window_handle:
            print("未找到游戏窗口，无法截图")
            return None
        
        # 获取窗口尺寸
        x, y, x2, y2 = win32gui.GetWindowRect(self.window_handle)
        width, height = x2 - x, y2 - y
        
        # 创建设备上下文
        hwnd_dc = win32gui.GetWindowDC(self.window_handle)
        mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
        save_dc = mfc_dc.CreateCompatibleDC()
        
        # 创建位图对象
        save_bitmap = win32ui.CreateBitmap()
        save_bitmap.CreateCompatibleBitmap(mfc_dc, width, height)
        save_dc.SelectObject(save_bitmap)
        
        # 创建位图对象
        save_bitmap = win32ui.CreateBitmap()
        save_bitmap.CreateCompatibleBitmap(mfc_dc, width, height)
        save_dc.SelectObject(save_bitmap)
        
        # 复制窗口内容到位图
        save_dc.BitBlt((0, 0), (width, height), mfc_dc, (0, 0), win32con.SRCCOPY)
        
        # 将位图转换为PIL图像
        bmpinfo = save_bitmap.GetInfo()
        bmpstr = save_bitmap.GetBitmapBits(True)
        img = Image.frombuffer(
            'RGB',
            (bmpinfo['bmWidth'], bmpinfo['bmHeight']),
            bmpstr, 'raw', 'BGRX', 0, 1)
        
        # 清理资源
        win32gui.DeleteObject(save_bitmap.GetHandle())
        save_dc.DeleteDC()
        mfc_dc.DeleteDC()
        win32gui.ReleaseDC(self.window_handle, hwnd_dc)
        
        # 转换为OpenCV格式并确保图像数据有效
        cv_img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
        if cv_img is None or cv_img.size == 0:
            print("截图获取失败：图像数据无效")
            return None
        return cv_img
    
    def find_image(self, template_name, screenshot=None):
        """在游戏截图中查找模板图像
        
        Args:
            template_name: 模板图像文件名（不含路径）
            screenshot: 可选的游戏截图，如果为None则自动截图
            
        Returns:
            tuple: (中心x坐标, 中心y坐标, 匹配度) 如果找到图像，否则返回None
        """
        template_path = os.path.join(self.templates_dir, template_name)
        if not os.path.exists(template_path):
            print(f"模板图像不存在: {template_path}")
            return None
        
        # 加载模板图像
        try:
            with open(template_path, 'rb') as f:
                img_array = np.asarray(bytearray(f.read()), dtype=np.uint8)
                template = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
                if template is None:
                    print(f"无法加载模板图像: {template_path}，图像格式可能不支持")
                    return None
        except Exception as e:
            print(f"无法加载模板图像: {template_path}，错误: {str(e)}")
            return None
        
        # 获取游戏截图
        if screenshot is None:
            screenshot = self.screenshot()
            if screenshot is None:
                return None
        
        # 使用模板匹配
        result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        
        if max_val >= self.confidence_threshold:
            # 计算中心点坐标
            h, w = template.shape[:2]
            center_x = max_loc[0] + w // 2
            center_y = max_loc[1] + h // 2
            return (center_x, center_y, max_val)
        
        return None
    
    def click(self, x, y, button='left'):
        """点击指定坐标
        
        Args:
            x: 屏幕x坐标
            y: 屏幕y坐标
            button: 鼠标按钮，'left'或'right'
        """
        if not self.window_handle:
            print("未找到游戏窗口，无法点击")
            return
        
        # 获取窗口位置
        window_x, window_y, _, _ = win32gui.GetWindowRect(self.window_handle)
        
        # 计算绝对坐标
        abs_x = window_x + x
        abs_y = window_y + y
        
        # 执行点击
        pyautogui.click(abs_x, abs_y, button=button)
        print(f"点击坐标: ({x}, {y}) -> 屏幕坐标: ({abs_x}, {abs_y})")
    
    def click_image(self, template_name, screenshot=None):
        result = self.find_image(template_name, screenshot)
        if result:
            x, y, confidence = result
            print(f"[点击模板] 名称: {template_name}, 位置: ({x}, {y}), 匹配度: {confidence:.2f}")
            
            # 获取模板图像
            template_path = os.path.join(self.templates_dir, template_name)
            try:
                with open(template_path, 'rb') as f:
                    img_array = np.asarray(bytearray(f.read()), dtype=np.uint8)
                    template = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
                    if template is None:
                        print(f"无法加载模板图像: {template_path}，图像格式可能不支持")
                        return False
            except Exception as e:
                print(f"无法加载模板图像: {template_path}，错误: {str(e)}")
                return False
            
            # 获取模板图像的宽度和高度
            h, w = template.shape[:2]
            
            # 计算模板图像的中心区域
            center_x = x
            center_y = y
            half_w = w // 2
            half_h = h // 2
            
            # 在中心区域内随机选择一个点击点
            random_x = random.randint(center_x - half_w, center_x + half_w)
            random_y = random.randint(center_y - half_h, center_y + half_h)
            
            # 执行点击
            self.click(random_x, random_y)
            return True
        else:
            # print(f"[未找到模板] 名称: {template_name}")
            return False
    
    def save_template(self, template_name, region=None, save_dir=None):
        """保存当前屏幕区域作为模板
        
        Args:
            template_name: 模板名称（不含扩展名）
            region: 区域 (x, y, width, height)，None表示全屏
            save_dir: 保存目录的路径，None表示使用默认目录
            
        Returns:
            bool: 是否成功保存
        """
        screenshot = self.screenshot()
        if screenshot is None:
            print(f"[保存模板失败] 名称: {template_name}, 原因: 无法获取截图")
            return False
        
        try:
            if region:
                x, y, w, h = region
                # 获取游戏窗口客户区矩形
                # 检查窗口句柄是否有效
                if self.window_handle is None:
                    raise ValueError("窗口句柄无效")
                client_rect = win32gui.GetClientRect(int(self.window_handle))
                client_x, client_y = win32gui.ClientToScreen(self.window_handle, (0, 0))
                client_width, client_height = client_rect[2], client_rect[3]
                
                # 调整区域坐标到客户区坐标系
                x += client_x
                y += client_y
                
                # 确保坐标不越界
                x = max(client_x, min(x, client_x + client_width))
                y = max(client_y, min(y, client_y + client_height))
                w = min(w, client_width - (x - client_x))
                h = min(h, client_height - (y - client_y))
                
                if w <= 0 or h <= 0:
                    print(f"[保存模板失败] 名称: {template_name}, 原因: 无效的截图区域")
                    return False
                screenshot = screenshot[y-client_y:y-client_y+h, x-client_x:x-client_x+w]
            
            template_path = os.path.join(self.templates_dir, f"{template_name}.png")
            
            # 使用Pillow保存图像并处理中文路径
            Image.fromarray(cv2.cvtColor(screenshot, cv2.COLOR_BGR2RGB))\
                .save(template_path.encode('utf-8'), 'PNG')
            
            print(f"[保存模板成功] 名称: {template_name}, 路径: {template_path}")
            return True
        except Exception as e:
            print(f"[保存模板失败] 名称: {template_name}, 原因: {str(e)}")
            return False
    
    def start_auto(self):
        """开始自动化流程"""
        if not self.window_handle:
            print("未找到游戏窗口，无法启动自动化")
            return
        
        self.running = True
        print("开始自动化流程...")
        
        try:
            while self.running:
                # 获取当前屏幕截图
                screenshot = self.screenshot()
                if screenshot is None:
                    continue
                
                # 获取模板目录中的所有模板文件
                templates_dir = self.templates_dir
                template_files = [f for f in os.listdir(templates_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
                
                # 循环遍历模板文件并依次进行比对和点击操作
                for template_file in template_files:
                    if self.click_image(template_file, screenshot):
                        print(f"点击了模板: {template_file}")
                        time.sleep(self.click_interval)
                        break  # 找到并点击一个模板后跳出循环，继续下一次截图
                else:
                    # 如果没有找到任何可点击的元素，等待一段时间
                    time.sleep(0.5)
                    
        except KeyboardInterrupt:
            print("用户中断，停止自动化")
            self.running = False
    
    def stop_auto(self):
        """停止自动化流程"""
        self.running = False
        print("已停止自动化流程")

class OnmyojiAutoGUI:
    def __init__(self):
        # 初始化主窗口
        self.root = tk.Tk()
        
        # 初始化树形控件选择相关的属性

        self.root.title("自动化工具")
        # 窗体的宽和高设置
        self.root.geometry("800x1080")
        self.root.resizable(True, True)
        
        # 设置模板目录
        self.templates_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
        if not os.path.exists(self.templates_dir):
            os.makedirs(self.templates_dir)
        
        # 初始化 selection_vars 字典
        self.selection_vars = {}
        
        # 创建菜单栏
        self.menu_bar = tk.Menu(self.root)
        self.root.config(menu=self.menu_bar)
        
        # 添加模板管理菜单
        self.template_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.template_menu.add_command(label="新建模板 (F7)", command=self.save_template)
        self.template_menu.add_command(label="重命名", command=self.rename_selected_template)
        self.template_menu.add_command(label="删除当前", command=self.delete_selected_template)
        self.template_menu.add_command(label="批量删除", command=self.batch_delete_templates)
        self.menu_bar.add_cascade(label="模板管理", menu=self.template_menu)
        
        # 添加自动化操作菜单
        self.auto_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.auto_menu.add_command(label="开始自动化 (F5)", command=self.start_auto)
        self.auto_menu.add_command(label="停止自动化 (F6)", command=self.stop_auto)
        self.menu_bar.add_cascade(label="自动化操作", menu=self.auto_menu)
        
        self.auto = OnmyojiAuto()
        self.setup_gui()
        
        # 更新窗口状态
        self.update_window_status()
        
        # 在初始化时更新模板预览
        self.update_template_previews()
        
        # 绑定快捷键
        self.root.bind('<F7>', lambda event: self.save_template())  # 新增绑定 F7 快捷键
        self.root.bind('<F5>', lambda event: self.start_auto())
        self.root.bind('<F6>', lambda event: self.stop_auto())
        self.create_system_tray_icon()  # 确保调用 create_system_tray_icon 方法

        self.window_list = []
        self.window_var = tk.StringVar()
        self.window_var.set("")  # 默认值
        self.setup_window_selection()

        # 初始化全局键盘监听
        self.start_global_hotkeys()

        # 创建树形控件
        tree_frame = ttk.Frame(self.root)
        tree_frame.pack(side='left', fill='y', padx=5, pady=5)
        
        self.template_tree = TemplateTreeView(tree_frame, self.templates_dir)
        
        # 创建树形控件的右键菜单
        self.tree_menu = tk.Menu(self.root, tearoff=0)
        self.tree_menu.add_command(label="新建目录", command=self.template_tree.create_directory)
        self.tree_menu.add_command(label="重命名", command=lambda: self.template_tree.start_rename(self.template_tree.current_selected_item))
        self.template_tree.tree.bind('<Button-3>', self.show_tree_menu)

    def show_tree_menu(self, event):
        """显示右键菜单"""
        item = self.template_tree.tree.identify('item', event.x, event.y)
        if item:
            self.template_tree.tree.selection_set(item)
            self.tree_menu.post(event.x_root, event.y_root)

    def update_template_previews(self):
        """更新模板预览，现在基于树形控件的选择"""
        if hasattr(self.template_tree, 'current_selected_item'):
            selected_path = self.template_tree._get_full_path(self.template_tree.current_selected_item)
            if os.path.isdir(selected_path):
                super().update_template_previews(selected_path)
        else:
            super().update_template_previews()

    def start_global_hotkeys(self):
        # 绑定全局快捷键
        keyboard.add_hotkey('ctrl+shift+a', self.start_auto)
        keyboard.add_hotkey('ctrl+shift+s', self.stop_auto)
        keyboard.add_hotkey('ctrl+shift+t', self.save_template)

    

    def create_system_tray_icon(self):
        """创建系统托盘图标"""
        try:
            # 加载托盘图标图像
            icon_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icon.png")
            if not os.path.exists(icon_path):
                print("托盘图标文件不存在，使用默认图标")
                icon_image = Image.new('RGBA', (64, 64), color="blue")  # 修改为 RGBA 模式以兼容 pystray
            else:
                icon_image = Image.open(icon_path)

            # 创建托盘菜单
            menu = (
                pystray.MenuItem('显示窗口', lambda: self.root.deiconify()),
                pystray.MenuItem('退出程序', self.quit_from_tray)
            )

            # 创建托盘图标
            self.tray_icon = pystray.Icon("OnmyojiAuto")
            self.tray_icon.menu = pystray.Menu(*menu)
            self.tray_icon.icon = icon_image
            self.tray_icon.title = "自动化工具"

            # 在单独线程中运行托盘图标
            import threading
            self.tray_thread = threading.Thread(target=self.tray_icon.run)
            self.tray_thread.daemon = True
            self.tray_thread.start()

            # 启动全局快捷键监听
            self.start_global_hotkeys()  # 确保快捷键监听启动
        except Exception as e:
            print(f"创建系统托盘图标失败: {str(e)}")

    

    def quit_from_tray(self, icon, item):
        """从托盘退出程序"""
        self.auto.stop_auto()  # 停止自动化流程
        self.root.quit()  # 退出主窗口
        icon.stop()  # 停止托盘图标
        # 停止全局快捷键监听
        keyboard.unhook_all()  # 确保快捷键监听停止
        print("全局快捷键已停止")
        # 添加隐藏托盘图标
        if hasattr(self, 'tray_icon') and self.tray_icon:
            try:
                self.tray_icon.stop()  # 使用stop()方法停止托盘图标
            except Exception as e:
                print(f"停止托盘图标时出错: {str(e)}")

    def setup_gui(self):
        
        # 设置护眼主题色
        style = ttk.Style()
        style.configure("TFrame", background="#F5F6F7")
        style.configure("TLabelframe", background="#F5F6F7")
        style.configure("TLabelframe.Label", background="#F5F6F7", foreground="#2C3E50")
        style.configure("TButton", background="#ECF0F1", foreground="#2C3E50", padding=6)
        style.configure("TLabel", background="#F5F6F7", foreground="#2C3E50")
        
        # 添加边框和悬停效果样式
        style.configure("Template.TFrame", background="#FFFFFF", relief="groove", borderwidth=2, bordercolor="#D1D5DB")
        style.configure("Hover.TFrame", background="#E8F0FE", relief="solid", borderwidth=1)
        
        # 设置窗口背景色
        self.root.configure(bg="#F5F6F7")
        
        # 创建状态栏
        status_frame = ttk.Frame(self.root)
        status_frame.pack(fill="x", padx=15, pady=5)
        
        # 添加窗口状态标签
        self.status_label = ttk.Label(status_frame, text="窗口状态：未检测到游戏窗口", foreground="#E74C3C")
        self.status_label.pack(side="left")
        
        # 添加重新检测按钮
        self.refresh_btn = ttk.Button(status_frame, text="重新检测", command=self.refresh_window)
        self.refresh_btn.pack(side="right")
        
        # 创建控制按钮区域
        control_frame = ttk.LabelFrame(self.root, text="控制面板", padding="15")
        control_frame.pack(fill="x", padx=15, pady=10)
        
        # 添加控制按钮
        self.start_btn = ttk.Button(control_frame, text="开始自动化 (F5)", command=self.start_auto)
        self.start_btn.pack(side="left", padx=8)
        
        self.stop_btn = ttk.Button(control_frame, text="停止自动化 (F6)", command=self.stop_auto, state="disabled")
        self.stop_btn.pack(side="left", padx=8)
        
        # 修改按钮文本提示为 F7
        self.save_template_btn = ttk.Button(control_frame, text="新建模板 (F7)", command=self.save_template)
        self.save_template_btn.pack(side="left", padx=8)
        
        # 创建模板预览区域
        templates_frame = ttk.LabelFrame(self.root, text="模板预览", padding="15")
        templates_frame.pack(fill="both", expand=True, padx=15, pady=(0, 10))
        
        # 创建水平分隔的框架
        split_frame = ttk.Frame(templates_frame)
        split_frame.pack(fill="both", expand=True)
        
        # 创建左侧树形控件区域
        tree_frame = ttk.Frame(split_frame)
        tree_frame.pack(side="left", fill="y", padx=(0, 5))


        
        
        # 创建树形控件
        self.create_tree_structure()
        
        # 创建右侧模板预览区域
        preview_frame = ttk.Frame(split_frame)
        preview_frame.pack(side="right", fill="both", expand=True)
        
        # 创建滚动区域
        canvas = tk.Canvas(preview_frame)
        scrollbar = ttk.Scrollbar(preview_frame, orient="vertical", command=canvas.yview)
        self.templates_grid = ttk.Frame(canvas)
        
        # 配置滚动
        canvas.configure(yscrollcommand=scrollbar.set)
        self.canvas_window = canvas.create_window((0, 0), window=self.templates_grid, anchor="nw")
        
        # 布局
        scrollbar.pack(side="right", fill="y")
        canvas.pack(side="left", fill="both", expand=True)
        
        # 绑定事件
        self.templates_grid.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        canvas.bind("<Configure>", self.on_canvas_configure)
        # 创建日志区域
        log_frame = ttk.LabelFrame(self.root, text="运行日志", padding="15")
        log_frame.pack(fill="both", expand=True, padx=15, pady=(0, 10))

        # 创建日志文本区域，设置固定高度和滚动条
        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=10, bg="#FFFFFF", fg="#2C3E50")
        self.log_text.pack(fill="both", expand=True, padx=2, pady=2)
        self.log_text.see(tk.END)

        # 新增清空日志按钮
        clear_log_btn = ttk.Button(log_frame, text="清空日志", command=lambda: self.log_text.delete(1.0, tk.END))
        clear_log_btn.pack(side="bottom", pady=5)

        # 确保日志区域始终显示最新内容
        def update_log(*args):
            self.log_text.see(tk.END)
        self.log_text.vbar.bind("<ButtonRelease-1>", update_log)

        # 重定向标准输出到日志区域
        sys.stdout = self.StdoutRedirector(self.log_text)
    
    def start_auto(self):
        if not self.auto.window_handle:
            messagebox.showerror("错误", "未找到游戏窗口，请确保游戏已启动")
            return
        
        self.start_btn.config(state="disabled")
        self.stop_btn.config(state="normal")
        
        # 在新线程中运行自动化流程
        import threading
        self.auto_thread = threading.Thread(target=self.auto.start_auto)
        self.auto_thread.daemon = True
        self.auto_thread.start()
    
    def stop_auto(self):
        self.auto.stop_auto()
        self.start_btn.config(state="normal")
        self.stop_btn.config(state="disabled")
        self.save_template_btn.config(state="normal")
    
    def refresh_window(self):
        """重新检测游戏窗口"""
        self.auto._find_game_window()
        self.update_window_status()
    
    def update_window_status(self):
        """更新窗口状态显示"""
        if self.auto.window_handle:
            window_title = win32gui.GetWindowText(self.auto.window_handle)
            self.status_label.config(
                text=f"窗口状态：已找到游戏窗口 (名称: {window_title}, 句柄: {self.auto.window_handle})",
                foreground="#27AE60"
            )
        else:
            self.status_label.config(
                text="窗口状态：未检测到游戏窗口",
                foreground="#E74C3C"
            )
    
    def save_template(self):
        # 生成GUID作为模板名称
        template_name = str(uuid.uuid4())
        
        # 检查文件名是否合法
        if not re.match(r'^[\w\-]+$', template_name):  # 支持字母、数字、下划线和连字符
            messagebox.showerror("错误", "生成的模板名称无效")
            return
        
        # 检查是否已存在同名模板
        template_path = os.path.join(self.auto.templates_dir, f"{template_name}.png")
        if os.path.exists(template_path):
            messagebox.showerror("错误", f"模板 {template_name} 已存在，请重试")
            return
        
        self.start_capture(template_name)
        self.update_template_previews()

    
    def update_template_previews(self, directory=None):
        """更新模板预览
        Args:
            directory: 要显示的模板目录路径，如果为None则显示根目录
        """
        try:
            # 清除现有预览
            for widget in self.templates_grid.winfo_children():
                widget.destroy()
            

            # 使用指定目录或默认目录
            templates_dir = directory if directory else self.auto.templates_dir
            if not os.path.exists(templates_dir):
                ttk.Label(self.templates_grid, text="模板目录不存在").pack(pady=10)
                return
    
            # 获取指定目录下的所有模板文件并按创建时间倒序排序
            try:
                template_files = [f for f in os.listdir(templates_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
                template_files.sort(key=lambda x: os.path.getctime(os.path.join(templates_dir, x)), reverse=True)
                if not template_files:
                    ttk.Label(self.templates_grid, text="当前目录下暂无模板").pack(pady=10)
                    return
            except Exception as e:
                print(f"读取目录 {templates_dir} 失败: {str(e)}")
                ttk.Label(self.templates_grid, text=f"读取目录失败: {str(e)}").pack(pady=10)
                return
    
            # 设置网格布局参数
            preview_size = 180  # 固定预览尺寸
            columns = 3
    
            # 设置列权重实现均匀分布
            for col in range(columns):
                self.templates_grid.grid_columnconfigure(col, weight=1)
    
            # 添加模板预览
            for i, template_file in enumerate(template_files):
                try:
                    # 创建带边框的预览框架
                    outer_frame = ttk.Frame(self.templates_grid, style="Template.TFrame")
                    outer_frame.grid(row=i // columns, column=i % columns, padx=5, pady=5, sticky="nsew")
    
                    # 创建内部框架用于显示图像和名称
                    inner_frame = ttk.Frame(outer_frame, style="Template.TFrame")
                    inner_frame.pack(expand=True, fill="both", padx=2, pady=2)
    
                    # 加载模板图像
                    template_path = os.path.join(templates_dir, template_file)
                    if not os.path.exists(template_path):
                        print(f"模板文件不存在: {template_path}")
                        continue
    
                    img = Image.open(template_path)
                    width, height = img.size
    
                    # 创建固定大小的白色背景
                    preview_img = Image.new('RGB', (preview_size, preview_size), 'white')
    
                    # 计算缩放比例并保持宽高比
                    ratio = min(preview_size/width, preview_size/height)
                    new_size = (int(width*ratio), int(height*ratio))
    
                    # 缩放原始图像
                    resized_img = img.resize(new_size, Image.Resampling.LANCZOS)
    
                    # 计算居中位置
                    x = (preview_size - new_size[0]) // 2
                    y = (preview_size - new_size[1]) // 2
    
                    # 将缩放后的图像粘贴到白色背景上
                    preview_img.paste(resized_img, (x, y))
    
                    # 创建预览图片
                    photo = ImageTk.PhotoImage(preview_img)
    
                    # 创建图片标签
                    label = ttk.Label(inner_frame, image=photo, style="TLabel")
                    setattr(label, '_photo', photo)  # 使用setattr保持图片引用，防止被垃圾回收
                    label.pack(expand=True)
    
                    # 添加模板名称和尺寸标签
                    base_name, ext = os.path.splitext(template_file)
                    size_text = f"{width}x{height}"
    
                    # 创建可编辑的名称输入框
                    name_entry = ttk.Entry(inner_frame, width=20)
                    name_entry.insert(0, base_name)
                    name_entry.bind("<FocusOut>", lambda e, entry=name_entry, old_name=base_name, ext=ext: self.save_renamed_template(entry, old_name, ext))
                    name_entry.bind("<Return>", lambda e, entry=name_entry, old_name=base_name, ext=ext: self.save_renamed_template(entry, old_name, ext))
                    name_entry.bind("<Escape>", lambda e, entry=name_entry, old_name=base_name: entry.delete(0, tk.END) or entry.insert(0, old_name))
                    name_entry.bind("<Button-1>", lambda e, entry=name_entry: entry.select_range(0, tk.END))
                    name_entry.pack(pady=5)
                    name_entry.config(state="disabled")  # 默认不可编辑
    
                    # 添加复选框
                    var = tk.BooleanVar()
                    check = ttk.Checkbutton(inner_frame, variable=var)
                    check.place(relx=1.0, rely=0.0, anchor='ne')
                    self.selection_vars[template_file] = var
    
                    for widget in (label, name_entry):
                        # widget.bind("<Button-1>", lambda e, path=template_path: self.show_template(path))
                        widget.bind("<Button-3>", lambda e, name=template_file: self.show_template_menu(e, name))
                        widget.bind("<Enter>", lambda e, frame=outer_frame: frame.configure(style="Hover.TFrame"))
                        widget.bind("<Leave>", lambda e, frame=outer_frame: frame.configure(style="Template.TFrame"))
    
                except Exception as e:
                    print(f"加载模板 {template_file} 失败: {str(e)}")
                    continue
    
        finally:
            # 确保在任何情况下都能更新网格布局
            self.templates_grid.update_idletasks()

    def save_renamed_template(self, entry, old_name, ext):
        new_name = entry.get().strip()
        if new_name and new_name != old_name:
            new_name_with_ext = f"{new_name}{ext}"
            template_path = os.path.join(self.auto.templates_dir, f"{old_name}{ext}")
            new_template_path = os.path.join(self.auto.templates_dir, new_name_with_ext)
            try:
                os.rename(template_path, new_template_path)
                entry.delete(0, tk.END)
                entry.insert(0, new_name)
                self.update_template_previews()
            except Exception as e:
                messagebox.showerror("错误", f"重命名模板失败: {str(e)}")
                entry.delete(0, tk.END)
                entry.insert(0, old_name)

    def start_capture(self, template_name):
        # 创建全屏透明窗口
        self.capture_window = tk.Toplevel(self.root)
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        self.capture_window.geometry(f"{screen_width}x{screen_height}+0+0")
        self.capture_window.attributes('-alpha', 0.3, '-fullscreen', True, '-topmost', True)
        self.capture_window.configure(bg='black')
        
        # 初始化截图区域坐标
        self.start_x = None
        self.start_y = None
        self.current_rect = None
        
        # 保存模板名称为实例变量
        self.template_name = template_name
        
        # 绑定鼠标事件
        self.capture_window.bind('<Button-1>', lambda e: self.on_mouse_down(e))
        self.capture_window.bind('<B1-Motion>', lambda e: self.on_mouse_move(e))
        self.capture_window.bind('<ButtonRelease-1>', lambda e: self.on_mouse_up(e, self.template_name))
        self.capture_window.bind('<Escape>', lambda e: self.cancel_capture())
        
        # 创建画布
        self.canvas = tk.Canvas(self.capture_window, highlightthickness=0)
        self.canvas.pack(fill='both', expand=True)
        self.canvas.configure(bg='black')
        
        # 创建工具栏
        self._create_toolbar()

    def _create_toolbar(self):
        # 创建工具栏框架
        self.toolbar = tk.Frame(self.capture_window, bg='#2C3E50')
        self.toolbar.place(relx=0.5, rely=1.0, anchor='s', y=-30)
        
        # 创建按钮
        btn_style = {'bg': '#34495E', 'fg': 'white', 'font': ('Arial', 12),
                    'relief': 'groove', 'borderwidth': 2, 'padx': 15, 'pady': 8}
        
        self.confirm_btn = tk.Button(self.toolbar, text='✓ 确定', command=lambda: self.on_mouse_up(None, self.template_name),
                  **{**btn_style, 'bg':'#4CAF50', 'activebackground':'#45a049'})
        self.confirm_btn.pack(side='left', padx=8)
        
        self.cancel_btn = tk.Button(self.toolbar, text='✕ 取消', command=self.cancel_capture,
                  **{**btn_style, 'bg':'#f44336', 'activebackground':'#da190b'})
        self.cancel_btn.pack(side='left', padx=8)
        
    def _update_magnifier(self, x, y):
        # 创建或更新放大镜
        if not self.magnifier:
            self.magnifier = self.canvas.create_oval(
                x-50, y-50, x+50, y+50,
                outline='#4FC3F7', width=2
            )
            # 创建十字线
            self.magnifier_lines = [
                self.canvas.create_line(x, y-50, x, y+50,
                    fill='#4FC3F7', width=1, dash=(2,2)),
                self.canvas.create_line(x-50, y, x+50, y,
                    fill='#4FC3F7', width=1, dash=(2,2))
            ]
        else:
            # 更新放大镜位置
            self.canvas.coords(self.magnifier, x-50, y-50, x+50, y+50)
            # 更新十字线位置
            self.canvas.coords(self.magnifier_lines[0], x, y-50, x, y+50)
            self.canvas.coords(self.magnifier_lines[1], x-50, y, x+50, y)
    
    def on_mouse_hover(self, event):
        # 获取游戏窗口位置
        # 检查窗口句柄是否有效
        if self.auto.window_handle is None:
            return None
        window_rect = win32gui.GetWindowRect(int(self.auto.window_handle))
        wx1, wy1, wx2, wy2 = window_rect
        
        # 检查鼠标是否在窗口区域内
        if wx1 <= event.x <= wx2 and wy1 <= event.y <= wy2:
            # 如果窗口区域发生变化，重新绘制边框
            if self.window_rect != window_rect:
                self.window_rect = window_rect
                # 清除现有边框
                self.canvas.delete('window_border')
                # 绘制半透明高亮区域
                self.canvas.create_rectangle(
                    wx1, wy1, wx2, wy2,
                    fill='#4FC3F7', stipple='gray25',
                    tags=('window_border', 'highlight'),
                    width=0
                )
                # 绘制动态边框
                self.canvas.create_rectangle(
                    wx1, wy1, wx2, wy2,
                    outline='#4FC3F7', width=2,
                    dash=(5,5), tags=('window_border', 'border')
                )
                # 创建边框动画
                self.canvas.after(50, self._animate_border)
            
            # 更新放大镜
            self._update_magnifier(event.x, event.y)
        else:
            # 鼠标移出窗口区域时清除高亮
            self.canvas.delete('window_border')
            self.window_rect = None
            
    def _animate_border(self):
        if self.window_rect:
            # 更新虚线偏移以创建动画效果
            current_offset = self.canvas.itemcget('border', 'dashoffset')
            new_offset = int(current_offset) - 1 if current_offset else 5
            self.canvas.itemconfig('border', dashoffset=new_offset)
            self.canvas.after(50, self._animate_border)
    
    def on_mouse_down(self, event):
        self.start_x = event.x
        self.start_y = event.y
        
        # 创建选择框
        self.current_rect = self.canvas.create_rectangle(
            self.start_x, self.start_y,
            self.start_x, self.start_y,
            outline='#00C853',  # 翠绿色
            width=2,
            fill='#00C853',  # 使用标准的16进制RGB颜色值
            stipple='gray25'  # 使用stipple实现半透明效果
        )
        
        # 创建尺寸标签
        self.size_label = self.canvas.create_text(
            self.start_x, self.start_y,
            text='0 x 0',
            fill='#00C853',
            font=('Arial', 10, 'bold')
        )
    
    def on_mouse_move(self, event):
        if self.current_rect:
            # 根据Shift键状态判断是否保持比例
            width = event.x - self.start_x
            height = event.y - self.start_y
            
            if event.state & 0x1:  # 检测Shift键按下
                side_length = max(abs(width), abs(height))
                width = side_length * (1 if width > 0 else -1)
                height = side_length * (1 if height > 0 else -1)
            
            # 计算实际坐标
            end_x = self.start_x + width
            end_y = self.start_y + height
            
            # 更新选择框
            self.canvas.coords(self.current_rect,
                self.start_x or 0, self.start_y or 0,
                end_x, end_y)
            
            # 更新尺寸标签
            actual_width = abs(end_x - self.start_x)
            actual_height = abs(end_y - self.start_y)
            self.canvas.itemconfig(self.size_label, text=f'{actual_width} x {actual_height}')
            
            # 更新尺寸标签位置
            # 确保坐标值不为None后再计算最大值
            label_x = max(self.start_x or 0, end_x or 0) - 5
            # 确保坐标值不为None后再计算最大值
            label_y = max(self.start_y or 0, end_y or 0) - 5
            self.canvas.coords(self.size_label, label_x, label_y)
    
    def on_mouse_up(self, event, template_name):
        # 检查 self.canvas 和 self.current_rect 是否存在
        if not hasattr(self, 'canvas') or not hasattr(self, 'current_rect') or not self.current_rect:
            print("警告: 画布或选择框未初始化，无法完成操作")
            return

        try:
            # 获取选择区域坐标
            x1, y1, x2, y2 = self.canvas.coords(self.current_rect)
            region = (int(min(x1, x2)), int(min(y1, y2)),
                     int(abs(x2-x1)), int(abs(y2-y1)))
            
            # 关闭截图窗口
            self.capture_window.destroy()
            
            # 保存模板
            if self.auto.save_template(template_name, region):
                messagebox.showinfo("成功", f"模板 {template_name} 保存成功")
                self.update_template_previews()
            else:
                messagebox.showerror("错误", "保存模板失败")
        except Exception as e:
            print(f"处理鼠标释放事件时出错: {str(e)}")

    def cancel_capture(self):
        if hasattr(self, 'capture_window'):
            # 销毁截图窗口并清理相关资源
            self.capture_window.destroy()
            if hasattr(self, 'canvas'):
                del self.canvas
            if hasattr(self, 'current_rect'):
                del self.current_rect
    
    class StdoutRedirector:
        def __init__(self, text_widget):
            self.text_widget = text_widget
        
        def write(self, string):
            self.text_widget.insert(tk.END, string)
            self.text_widget.see(tk.END)
        
        def flush(self):
            pass
    
    def run(self):
        # 修改窗口关闭行为：隐藏窗口而不是直接退出
        self.root.protocol("WM_DELETE_WINDOW", self.hide_to_tray)
        self.root.mainloop()
    
    def hide_to_tray(self):
        """隐藏主窗口到托盘"""
        self.root.withdraw()  # 隐藏窗口

    def on_canvas_configure(self, event):
        # 调整画布窗口宽度以适应画布
        self.templates_grid.grid_columnconfigure(0, weight=1)
        canvas_width = event.width
        self.templates_grid.update_idletasks()
        self.root.update_idletasks()
        canvas = event.widget
        canvas.itemconfig(self.canvas_window, width=canvas_width)

        # 确保 template_path 存在且有效
        template_path = os.path.join(self.auto.templates_dir, "example_template.png")  # 假设这是默认模板路径
        if not os.path.exists(template_path):
            print(f"模板文件不存在: {template_path}")
            return

        # 打开图像文件
        img = Image.open(template_path)
        window = tk.Toplevel(self.root)
        window.title("模板预览")
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        window.update_idletasks()
        width = window.winfo_width()
        height = window.winfo_height()
        x = (screen_width - width) // 2
        y = (screen_height - height) // 3
        # 计算相对于主窗体的位置
        main_x = self.root.winfo_x()
        main_y = self.root.winfo_y()
        window.geometry(f"+{main_x + x}+{main_y + y}")
        
        # 创建滚动画布
        canvas = tk.Canvas(window, highlightthickness=0)
        scrollbar = ttk.Scrollbar(window, orient="vertical", command=canvas.yview)
        canvas.configure(yscrollcommand=scrollbar.set)
        
        # 创建内容框架
        content_frame = ttk.Frame(canvas)
        canvas_window = canvas.create_window((0, 0), window=content_frame, anchor="nw")
        
        # 创建图像标签
        photo = ImageTk.PhotoImage(img)
        label = ttk.Label(content_frame)
        label.configure(image=photo)
        # 保存对图片对象的引用，防止被垃圾回收
        setattr(label, '_image', photo)
        label.pack(padx=10, pady=10)
        
        # 添加关闭按钮
        ttk.Button(content_frame, text="关闭", command=window.destroy).pack(pady=5)
        
        # 布局滚动画布
        scrollbar.pack(side="right", fill="y")
        canvas.pack(side="left", fill="both", expand=True)
        
        # 绑定事件
        content_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        canvas.bind("<Configure>", lambda e: canvas.itemconfig(canvas_window, width=e.width))
        
        # 绑定鼠标滚轮事件
        def on_mousewheel(event):
            delta = -1 * (event.delta // 120)
            canvas.yview_scroll(delta, "units")
            return "break"
        
        # 为所有相关部件绑定滚轮事件
        canvas.bind("<MouseWheel>", on_mousewheel)
        content_frame.bind("<MouseWheel>", on_mousewheel)
        label.bind("<MouseWheel>", on_mousewheel)
    
    def show_template_menu(self, event, template_name):
        # 保存当前选中的模板
        self.selected_template = template_name
        # 显示右键菜单
        self.template_menu.post(event.x_root, event.y_root)

    def rename_selected_template(self):
        if hasattr(self, 'selected_template'):
            base_name, ext = os.path.splitext(self.selected_template)
            template_path = os.path.join(self.auto.templates_dir, self.selected_template)
            for widget in self.templates_grid.winfo_children():
                for inner_widget in widget.winfo_children():
                    if isinstance(inner_widget, ttk.Entry) and inner_widget.get() == base_name:
                        inner_widget.config(state="normal")
                        inner_widget.focus_set()
                        break
        else:
            messagebox.showwarning("提示", "请先选择一个模板")

    def batch_delete_templates(self):
        selected = [name for name, var in self.selection_vars.items() if var.get()]
        if not selected:
            messagebox.showwarning("提示", "请先选择要删除的模板")
            return
        
        if messagebox.askyesno("确认", f"确定要删除选中的 {len(selected)} 个模板吗？\n{', '.join(selected)}"):
            for name in selected:
                template_path = os.path.join(self.auto.templates_dir, name)
                try:
                    os.remove(template_path)
                except Exception as e:
                    messagebox.showerror("错误", f"删除模板 {name} 失败: {str(e)}")
            self.update_template_previews()

    def delete_selected_template(self):
        self.update_template_previews()
        if hasattr(self, 'selected_template'):
            base_name = os.path.splitext(self.selected_template)[0]
        else:
            messagebox.showwarning("提示", "请先选择一个模板")
            return
        template_path = os.path.join(self.auto.templates_dir, f"{base_name}.png")
        try:
            # 添加删除确认
            if messagebox.askyesno("确认", f"确定要删除模板 {os.path.splitext(self.selected_template)[0]} 吗？"):
                os.remove(template_path)
                self.update_template_previews()
        except Exception as e:
            messagebox.showerror("错误", f"删除模板失败: {str(e)}")



    def setup_window_selection(self):
        # 创建窗口选择区域
        window_selection_frame = ttk.LabelFrame(self.root, text="选择游戏窗口", padding="15")
        window_selection_frame.pack(fill="x", padx=15, pady=10)

        # 创建下拉菜单
        self.window_dropdown = ttk.Combobox(window_selection_frame, textvariable=self.window_var, state="readonly")
        self.window_dropdown.pack(side="left", padx=5)

        # 创建刷新按钮
        refresh_button = ttk.Button(window_selection_frame, text="刷新", command=self.refresh_window_list)
        refresh_button.pack(side="left", padx=5)

        # 创建选择按钮
        select_button = ttk.Button(window_selection_frame, text="选择", command=self.select_window)
        select_button.pack(side="left", padx=5)

        # 刷新窗口列表
        self.refresh_window_list()

    def refresh_window_list(self):
        self.window_list = self.enum_windows()
        self.window_var.set("")  # 清空当前选择
        self.window_dropdown['values'] = self.window_list

    def enum_windows(self):
        window_list = []

        def callback(hwnd, extra):
            if win32gui.IsWindowVisible(hwnd):
                title = win32gui.GetWindowText(hwnd)
                if title:
                    window_list.append(title)
            return True

        win32gui.EnumWindows(callback, None)
        return window_list

    def select_window(self):
        selected_title = self.window_var.get()
        if selected_title:
            self.auto.window_title = selected_title
            self.auto._find_game_window(selected_title)
            self.update_window_status()
        else:
            messagebox.showwarning("提示", "请选择一个窗口")

    def create_tree_structure(self):
        """创建并设置树形控件"""
        # 创建左侧树形控件区域
        tree_frame = ttk.Frame(self.root)
        tree_frame.pack(side='left', fill='y', padx=5, pady=5)
        
        # 创建模板目录树形控件
        self.template_tree = TemplateTreeView(tree_frame, self.templates_dir)
        
        # 创建右键菜单
        self.tree_menu = tk.Menu(self.root, tearoff=0)
        self.tree_menu.add_command(label="新建目录", command=self.template_tree.create_directory)
        self.tree_menu.add_command(label="重命名", command=lambda: self.template_tree.start_rename(self.template_tree.current_selected_item))
        self.template_tree.tree.bind('<Button-3>', self.show_tree_menu)
        
        # 绑定选择事件更新预览区域
        self.template_tree.tree.bind('<<TreeviewSelect>>', lambda e: self.update_template_previews())


def sync_time_with_ntp():
    """同步系统时间到NTP服务器"""
    ntp_servers = ['ntp.aliyun.com', 'time1.cloud.tencent.com', 'time.windows.com']
    for server in ntp_servers:
        try:
            client = ntplib.NTPClient()
            response = client.request(server)
            system_time = time.ctime(response.tx_time)  # 使用 time.ctime 替换 ctime
            print(f"同步时间到NTP服务器: {server} - {system_time}")
            # 设置系统时间（需要管理员权限）
           
            return  # 成功同步时间后退出函数
        except Exception as e:
            print(f"同步时间到NTP服务器 {server} 失败: {str(e)}")

    # 如果所有NTP服务器同步时间失败，弹窗提示用户
    messagebox.showerror("网络错误", "未连接到互联网，程序无法使用，请连接互联网之后重新尝试！")
    import sys  # 导入sys模块
    sys.exit(1)  # 所有服务器同步失败后退出程序
# def is_admin():
#     """检查当前进程是否具有管理员权限"""
#     try:
#         return ctypes.windll.shell32.IsUserAnAdmin()
#     except:
#         return False

def main():
    # 设置日志文件
    log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'app.log')
    logging.basicConfig(filename=log_file, level=logging.INFO,
                      format='%(asctime)s - %(levelname)s - %(message)s')
    
    logging.info("自动化工具启动中...")
    
    # 检查是否具有管理员权限
    # if not is_admin():
    #     # 重新启动脚本并请求管理员权限
    #     ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 0)
    #     sys.exit(0)

    # 创建等待窗口
    wait_root = tk.Tk()
    wait_root.withdraw()  # 隐藏等待窗口
    wait_root.title("启动提示")
    wait_root.geometry("300x100")
    wait_root.resizable(False, False)
    
    # 创建启动提示窗口
    splash = tk.Toplevel(wait_root)
    splash.title("启动提示")
    splash.geometry("300x100")
    splash.resizable(False, False)
    
    # 获取屏幕尺寸
    screen_width = splash.winfo_screenwidth()
    screen_height = splash.winfo_screenheight()
    
    # 计算窗口位置
    x = (screen_width - 300) // 2
    y = (screen_height - 100) // 2
    splash.geometry(f"+{x}+{y}")
    
    wait_label = tk.Label(splash, text="正在启动自动化工具，请稍候...", font=("Arial", 12))
    wait_label.pack(pady=20)
    
    try:
        sync_time_with_ntp()  # 获取ntp同步时间
    except Exception as e:
        logging.error(f"时间同步失败: {str(e)}")
    
    splash.destroy()  # 销毁启动提示窗口
    wait_root.destroy()  # 销毁主窗口
    # 创建主窗口
    gui = OnmyojiAutoGUI()
    gui.run()

if __name__ == "__main__":
    main()
    